
class _ObjectFormPlugin(_Plugin):

    def __init__(self, id, pluginType, path, rootElem):
        super().__init__(id, pluginType, path, rootElem)

        self._fileName = None
        self._className = None
        self._workerObj = None

        # get filename property
        if len(rootElem.xpath("./filename")) > 0:
            self._fileName = rootElem.xpath("./filename").text
        else:
            raise Exception("no filename property in plugin with id \"%s\"" % (id))

        # get classname property
        if len(rootElem.xpath("./classname")) > 0:
            self._className = rootElem.xpath("./classname").text
        else:
            raise Exception("no classname property in plugin with id \"%s\"" % (id))

        # plugin type specific processing
        if self._pluginType == WrtConst.PLUGIN_TYPE_PRIVNET:
            self.__parsePrivnetPlugin()
        elif self._pluginType == WrtConst.PLUGIN_TYPE_SUBNET:
            self.__parseSubNetPlugin()
        else:
            assert False

    def is_loaded(self):
        return self._workerObj is not None

    def load(self):
        assert self._workerObj is None
        modname, mod = Util.loadPythonFile(os.path.join(self._path, self._fileName))
        self._workerObj = exec("mod.%s" % (self._className))

    def unload(self):
        if self._workerObj is not None:
            self._workerObj.dispose()
            self._workerObj = None

    def __parsePrivnetPlugin(self):
        pass

    def __parseSubnetPlugin(self):
        pass

    def __getattr__(self, attr):
        if self._workerObj is None or attr.startswith("_"):
            raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr))
        else:
            return getattr(self._workerObj, attr)



class UrlOpenAsync(threading.Thread):

    def __init__(self, url, ok_callback, error_callback):
        super().__init__()

        self.url = url
        self.ok_callback = ok_callback
        self.error_callback = error_callback
        self.proc = None
        self.idleId = None
        self.bComplete = False

    def start(self):
        self.proc = subprocess.Popen(["/usr/bin/curl", self.url],
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
        super().start()

    def cancel(self):
        assert self.proc is not None and not self.bComplete

        if self.proc.poll() is None:
            self.proc.terminate()
            self.proc.wait()
        self.join()
        GLib.source_remove(self.idleId)

    def run(self):
        out, err = self.proc.communicate()
        out = out.decode("utf-8").replace("\n", "")
        err = err.decode("utf-8")
        if self.proc.returncode == 0:
            self.idleId = GLib.idle_add(self._idleCallback, self.ok_callback, out)
        else:
            self.idleId = GLib.idle_add(self._idleCallback, self.error_callback, self.proc.returncode, err)

    def _idleCallback(self, func, *args):
        try:
            func(*args)
        except BaseException:
            logging.error("Error occured in UrlOpenAsync idle callback", exc_info=True)
        finally:
            self.bComplete = True
            return False



        self._cfg = cfg
        self._callback = None

        # check self._cfg
        if "name" not in self._cfg:
            raise InvalidCfgError(cfg_path, "missing name property")

        # check self._cfg
        if self._cfg.get("auto-connect", "true") not in ["true", "false"]:
            raise InvalidCfgError(cfg_path, 'invalid value "%s" for auto-connect property' % (self._cfg["auto-connect"]))

                for i, line in enumerate(f):
                    line = line.strip()
                    if line.startswith("#") or line == "":
                        continue

                    tlist = line.split(" ")
                    if len(tlist) != 2:
                        raise bnetd.InvalidCfgError(bnetd.Const.CONFIG_CONNECTION_SETTINGS_FILE, "invalid line %d" % (i + 1))

                    try:
                        key = uuid.UUID(tlist[0])
                    except ValueError:
                        raise bnetd.InvalidCfgError(bnetd.Const.CONFIG_CONNECTION_SETTINGS_FILE, "invalid line %d" % (i + 1))

                    if tlist[1] == "true":
                        self._data[key] = True
                    elif tlist[1] == "false":
                        self._data[key] = False
                    else:
                        raise bnetd.InvalidCfgError(bnetd.Const.CONFIG_CONNECTION_SETTINGS_FILE, "invalid line %d" % (i + 1))


    def get_all_subnets(self):
        # return dict<subnet-id,subnet-data>
        # returns all the subnets
        pass

    def get_subnet(self, subnet_id):
        # return subnet-data
        """
        subnet-data example:
        {
            "bridge": {
                "ifname": "INTERFACE-NAME",
                "ifconfig": {
                    "inet": {
                        "address": "X.X.X.X",
                        "prefixlen": N,
                    },
                    "inet6": {
                        "address": "X.X.X.X",
                        "prefixlen": N,
                    },
                },
            },
            "subif": [ "INTERFACE-NAME", "INTERFACE-NAME" ],
        }
        """
        pass

    def get_subnet_clients(self, subnet_id):
        # return dict<client-id,client-data>
        # returns all the clients of the specified subnet
        pass

    def get_subnet_client(self, subnet_id, client_id):
        # return client-data
        pass

    def stop(self):
        assert False





class DefaultSelector:

    def __init__(self):
        self._cm = None
        self._conn_pending_list = []
        self._idle_id = None

    def dispose(self):
        if self._idle_id is not None:
            GLib.source_remove(self._idle_id)
            self._idle_id = None
        if True:
            self._conn_pending_list = []
        if True:
            self._cm = None

    def set_slaves(self, connection_manager):
        assert self._cm is None
        self._cm = connection_manager

    def on_obconn_available(self, plugin_id, connection_id, connection_data):
        self._conn_pending_list.append(connection_id)
        if self._idle_id is None:
            self._idle_id = GLib.idle_add(self._idle_callback)

    def on_obconn_unavailable(self, connection_id, reason):
        try:
            self._conn_pending_list.remove(connection_id)
        except ValueError:
            return

        if len(self._conn_pending_list) == 0:
            assert self._idle_id is not None
            GLib.source_remove(self._idle_id)
            self._idle_id = None

    def on_obconn_activated(self, connection_id, connection_livedata):
        pass

    def on_obconn_deactivated(self, connection_id, reason):
        pass

    def _idle_callback(self):
        try:
            for connection_id in self._conn_pending_list:
                self._cm.enable_connection(connection_id)
        except BaseException:
            logging.error("Error occured in DefaultSelectorLv01 idle callback", exc_info=True)
        finally:
            self._idle_id = None
            return False

